/*
 * TODO language 
 * Copyright (c) 2010 TODO Team
 * 
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */

#ifndef TODO_COMPILER_H
#define TODO_COMPILER_H
#include <stack>
#include "types.h"
#include "location.hh"
#include "opcodes.h"
#include "modules/module.h"

#define NEW_RESULT_IF_NOT_EXISTS(x)            \
	if ((x).data_type != todo::TODO_DYNAMIC) { \
		(x).result = new ValueResult;          \
	}
	
#define SET_RESULT(x, y)          \
	(x).data_type = TODO_DYNAMIC; \
	(x).result = y;               \
	(x).result_flag = REFERENCED;
	
struct blk_state {
	bool dead;  // The block contains dead code
	bool done;  // A true condition was reached
	bool pdead; // The parent state
	
	blk_state() : dead(false), done(false), pdead(false) { }
	blk_state(const blk_state& cp) {
		pdead = dead = cp.dead;
		done = false;
	}
};

#define IFNDEAD(_stmt)                             \
	if (compiler.blk_states.top().dead == false) { \
		_stmt;                                     \
	}
	
#define BEGIN_BLOCK \
	compiler.blk_states.push(blk_state(compiler.blk_states.top()))
	
#define END_BLOCK \
	compiler.blk_states.pop()
	
#define CHECK_BLOCK_STATE                                    \
	if (compiler.blk_states.top().pdead == false) {          \
		if (compiler.blk_states.top().done == true) {        \
			compiler.blk_states.top().dead = true;           \
			compiler.blk_states.top().done = false;          \
		} else if (compiler.blk_states.top().dead == true) { \
			compiler.blk_states.top().dead = false;          \
		}                                                    \
	}

namespace todo {

class Compiler {
	// Opcode list
	static std::vector<todo::opcode::Opcode*> opcode_list;
	// Arguments call stack
	std::stack<ArgList*> call_args;
	// Arguments declaration stack
	std::stack<FunctionArgList*> decl_args;
	// Modules
	TodoModules modules;
public:
	// Conditional block states
	std::stack<blk_state> blk_states;
	// Compiled files counter
	static int count;
	// Scope handler
	Scopes scopes;
	// Jump stacks
	std::stack<std::stack<opcode::Opcode*> > jmps;
	// Break stacks
	std::stack<std::stack<opcode::Opcode*> > brk_jmps;
	const yy::location* location;
	bool is_file;

	Compiler();
	~Compiler() { }

	void init(void);
	void addOpcode(todo::opcode::Opcode*);
	unsigned int get_op_num(void) const;
	opcode::Opcode* get_opcode(unsigned int) const;
	void error(const char*) const;
	void error(std::string) const;
	void finalize(void);
	void shutdown(void);
	void begin_fcall(void);
	void begin_fdecl(void);
	void add_arg(Data&);
	void add_decl_arg(Data&, Data&);
	void import(Data&) const;
	
	void check_exists_var(Data*, Data&, Data&);
	void register_func(Function*);
	bool check_type_var(const std::string*, const Data&);
	bool check_type_value(const Type*, const Data&);
	bool check_func_exists(const Data&);
	Var* get_var(const std::string*);
	
	void add_module(Data&, Data&);
	void end_module(Data&);
	void assign(Data&, Data&);
	void minus(Data&, Data&, Data*, bool);
	void plus(Data&, Data&, Data*, bool);
	void div(Data&, Data&, Data*, bool);
	void mult(Data&, Data&, Data*, bool);
	void mod(Data&, Data&, Data*, bool);
	void declare_var(Data&, Data&, Data*);
	void println(Data&, bool);
	void method_call(Data&, Data&, Data&);
	void static_method_call(Data&, Data&, Data&);
	void slice(Data&, Data*, Data*);
	void less(Data&, Data&);
	void greater(Data&, Data&);
	void less_equal(Data&, Data&);
	void greater_equal(Data&, Data&);
	void subscript(Data&, Data&);
	void pre_increment(Data&);
	void pre_decrement(Data&);
	void pos_increment(Data&);
	void pos_decrement(Data&);
	void if_cond(Data&, Data&);
	void if_elsif(Data&, Data&);
	void if_end_block(Data&);
	void end_if(void);
	void equal(Data&, Data&);
	void is_not_equal(Data&, Data&);
	void new_vector(Data&);
	void vector_add_item(Data&, Data&);
	void new_map(Data&);
	void map_add_item(Data&, Data&, Data&);
	void begin_loop(Data&);
	void start_loop(Data&);
	void start_loop_2(Data&);
	void end_loop(Data&, Data&);
	void end_loop_2(Data&, Data&, Data&);
	void pos_loop_next(Data&, Data&);
	void pop_brk_jmps(void);
	void add_func(Data*, Data&);
	void end_func(Data&);
	void func_call(Data&, Data& result);
	void negative(Data&);
	void return_stmt(Data*);
	void exit_stmt(Data*);
	void break_stmt(Data*);
	void xor_expr(Data&, Data&, Data*, bool);
	void or_expr(Data&, Data&, Data*, bool);
	void and_expr(Data&, Data&, Data*, bool);
	void shift_left(Data&, Data&, Data*, bool);
	void shift_right(Data&, Data&, Data*, bool);
private:
	inline GCValue* get_constant_value(const Data&) const;
	inline bool constant_folding(const std::string&, Data&, Data&, Data*) const;
	inline void clear_data_value(Data&) const;
	inline bool check_dead_condition(const Data&);
};

} // todo

extern todo::Compiler compiler;

#endif // TODO_COMPILER_H
